Lambda@EdgeによるIP制限をPythonで書いてみた
Lambda@Edgeで本番公開前のCloudFrontに対してIP制限を行う機会がありました。せっかくなので紹介させていただこうと思います。
Lambda@Edgeは今の所、Node.jsとPythonが対応しており、ググればそのまま流用できそうなスクリプトが見つかりましたが、いずれもNode.jsだったのでPythonで作成してみました。
前提
トリガーとなるCloudFrontディストリビューションは既に作成済みとします。今回はS3をオリジンとしています。
それでは手順を見ていきましょう。
関数の作成
今回はコンソールから作成します。適当な名前をつけて、ランタイムは「Python 3.8」を選びます。
ロールの指定は、「AWSポリシーテンプレートから新しいロールを作成」を選択します。ロール名は適当に入力してください。(今回はlambda-edge-restriction-role
という名前にしています)
「ポリシーテンプレート」のプルダウンから「基本的なLambda@Edgeのアクセス権限」を選択してください。関数を作成する時にIAMの信頼関係もLambda@Edgeに合った形で作成してくれます。
設定できたら「関数の作成」をクリックします。
次の画面で「関数コード」セクションにコードを書きます。入力できたら「Deploy」をクリックします。
(今回は単純で短いので、コンソールから書いてます)
コードの全体は下記です。
IP_WHITE_LIST
にアクセスを許可するIPをリストで記載しています。コードに記載しているIPはサンプルです。
import json CONTENT = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>403 Forbidden</title> </head> <body style="background-color:lightpink;"> <h1>This is an Error Page</h1> <p>Your IP is not allowed to access this site!</p> </body> </html> """ def lambda_handler(event, context): IP_WHITE_LIST = ['203.0.113.78', '198.51.100.29', '192.0.2.221'] request = event['Records'][0]['cf']['request'] client_ip = event['Records'][0]['cf']['request']['clientIp'] print("Client IP: ", client_ip) if client_ip in IP_WHITE_LIST: return request # return 403 response when clientIP doesn't exist in the whitelist response = { 'status': '403', 'statusDescription': 'Forbidden', 'headers': { 'cache-control': [ { 'key': 'Cache-Control', 'value': 'max-age=100' } ], "content-type": [ { 'key': 'Content-Type', 'value': 'text/html' } ], 'content-encoding': [ { 'key': 'Content-Encoding', 'value': 'UTF-8' } ] }, 'body': CONTENT } return response
Lambda関数のテスト
さて、ここでLambda関数が正しく動くか確認してみます。コンソールの「テスト」ボタンをクリックしてください。
テストイベントを設定します。イベント内容には下記のドキュメントにある「ビューワーリクエストの例」をそのまま利用しました。
下記はテストを実行した結果です。上記のイベント例に書かれている「203.0.113.178」は許可IPではないので、403エラーを返していることが確認できます。
トリガーの設定とデプロイ
スクリプト自体の動作が確認できたので、次にトリガーを設定していきます。
プルダウンからCloudFrontを選択します。
「トリガーの設定」で「Lambda@Edgeへのデプロイ」をクリックします。
次の画面では、対象のディストリビューションやビヘイビア、イベントの指定などを行います。今回はIP制限なので、「ビューワーリクエスト」を選択します。
設定が完了したら「デプロイ」をクリックして実行します。
(対象とするディストリビューション、ビヘイビアが複数ある場合は、この作業を繰り返します)
デプロイを実行すると、自動的に「バージョン1」が作成されて、指定したCloudFrontのビヘイビアに関連付けられます。
CloudFront側でステータスが変わっていることが確認できます。
ステータスが「Deployed」になれば完了です。
動作確認
先程のコードではサンプルIPしか許可していなかったので、まだどこからもアクセスできない状態になっています。ブラウザでアクセスして確認してみましょう。
下記の通り、Lambda@Edgeで「403エラーコード」が返されてコンテンツにアクセスできていないことが分かります。
では次に、許可したいIPを追加してみます。(画像ではxx.xx.xx.xxですが、実際には許可したいIPをシングルクォートで括って記載してください)
IP_WHITE_LIST = ['203.0.113.78', '198.51.100.29', '192.0.2.221','追加するIP']
IPを追記できたらコンソールの上部にあるメニューより「Lambda@Edgeへのデプロイ」をクリックします。
既存の関数を更新してデプロイする時は、オプションで「この関数で既存のCloudFrontトリガーを使用」を選択すると、以前指定した設定をそのまま引き継いで使用できるので、デプロイ作業が楽になります。
前回同様に対象が複数ある場合は、プルダウンから対象のトリガーを選択し直して同じ作業を繰り返します。
CloudFront側の更新が終わって、改めて確認するとS3に置いたコンテンツを見ることができました。
最後に
Pythonのスクリプトだけ紹介して終わろうかと思いましたが、それだけでは寂しいので、コンソールを利用した更新方法も踏まえてご紹介してみました。
以上です。